//=============================================================================
/**
 * @file	ice_move.c
 * @brief	氷塊移動処理
 * @author	saito
 *
 * モジュール名：IM_
 *
 */
//=============================================================================

#include "ice_move.h"
#include "system/snd_tool.h"	//for SE

#define ICE_HIT_NONE		(0)		//何もヒットしてない
#define ICE_HIT_WALL		(1)		//壁にヒット
#define ICE_HIT_OBJ			(2)		//OBJにヒット
#define ICE_HIT_NOICE_ATR	(4)		//氷床以外のアトリビュート検出


typedef struct ICE_MV_WORK_tag
{
	FIELD_OBJ_PTR Ice;
	FIELD_OBJ_PTR HitIce;
	u8 MvDir;
	u8 Seq;
	u8 dummy[2];
}ICE_MV_WORK;

typedef enum {
	ICE_MOVE_SLIDE,
	ICE_MOVE_PAUSE_ON_WAIT,
	ICE_MOVE_PAUSE_OFF_WAIT,
}ICE_MOVE;

static BOOL GMEVENT_MoveIceEvent(GMEVENT_CONTROL * event);
static FIELD_OBJ_PTR GetPlayerFrontIce(FIELD_OBJ_PTR player_fldobj);
static FIELD_OBJ_PTR GetFldObj( CONST_FIELD_OBJ_SYS_PTR sys, int x, int y, int z );
static BOOL MoveIce(ICE_MV_WORK *imw);
static u32 IceHitCheck( FIELD_OBJ_PTR fldobj,
						const int inX, const int inY, const int inZ );
static int GetMoveAnmCode(const u8 inDir);
static void UnderForceMoveClear( PLAYER_STATE_PTR player );

//---------------------------------------------------------------------------
/**
 * @brief	氷塊の移動イベントセット
 *
 * @param	fsys		フィールドシステムポインタ
 *
 * @return	BOOL TRUE:イベント発生する　FALSE:イベント発生しない
 */
//---------------------------------------------------------------------------
BOOL IM_SetIceMoveEvent(FIELDSYS_WORK * fsys)
{
	ICE_MV_WORK *work;
	FIELD_OBJ_PTR player_fldobj;
	FIELD_OBJ_PTR fldobj;
	//前方チェック
	player_fldobj = Player_FieldOBJSearch(fsys->fldobjsys);
	fldobj = GetPlayerFrontIce(player_fldobj);
	if (fldobj != NULL){
		//イベントワーク作成
		work = sys_AllocMemoryLo(HEAPID_WORLD, sizeof(ICE_MV_WORK) );
		work->Ice = fldobj;
		work->HitIce = NULL;
		work->MvDir = FieldOBJ_DirDispGet( player_fldobj );
		work->Seq = ICE_MOVE_SLIDE;
		//イベント作成
		FieldEvent_Set(fsys, GMEVENT_MoveIceEvent, work);
		//自機がすべる氷にあたったＳＥ
		Snd_SePlay(SEQ_SE_GS_HYOUKAI_HIT);
		return TRUE;
	}
	return FALSE;
}

//---------------------------------------------------------------------------
/**
 * @brief	氷塊の移動イベント
 *
 * @param	event	イベントポインタ
 *
 * @return	BOOL TRUE:イベント終了	FALSE:イベント継続
 */
//---------------------------------------------------------------------------
static BOOL GMEVENT_MoveIceEvent(GMEVENT_CONTROL * event)
{
	FIELDSYS_WORK * fsys = FieldEvent_GetFieldSysWork(event);
	ICE_MV_WORK *imw = FieldEvent_GetSpecialWork(event);
	int *seq = FieldEvent_GetSeqWork(event);

	switch (*seq) {
	case 0:
		{
			FIELD_OBJ_PTR player_fldobj;
			//自機のアニメ終了待ち
			player_fldobj = Player_FieldOBJSearch(fsys->fldobjsys);
			if ( FieldOBJ_AcmdEndCheck( player_fldobj )  ){
				//氷すべるＳＥスタート
				Snd_SePlay(SEQ_SE_GS_HYOUKAI_SUBERU);
				(*seq)++;
			}
		}
		break;
	case 1:	//氷塊の移動
		if ( MoveIce(imw) ){
			//氷すべるＳＥをストップ
			Snd_SeStopBySeqNo( SEQ_SE_GS_HYOUKAI_SUBERU, 0 );
			(*seq)++;
		}
		break;
	case 2:
		//アニメ待ち
		if ( FieldOBJ_AcmdEndCheck( imw->Ice ) ){
			if (imw->HitIce == NULL){
				(*seq)++;
			}else{
				if (FieldOBJ_AcmdEndCheck( imw->HitIce ) ){
					(*seq)++;
				}
			}
		}
		break;
	case 3:	//終了
		//強制移動終了
		UnderForceMoveClear( fsys->player );
		//解放
		sys_FreeMemoryEz(imw);
		return TRUE;
	}
	return FALSE;
}

//--------------------------------------------------------------
/**
 * 自機の前方が押せる氷塊かをチェック
 * @param	player_fldobj	自機フィールドOBJポインタ
 *
 * @retval	FIELD_OBJ_PTR	NULLの場合は動かせる氷塊検出の失敗
 */
//--------------------------------------------------------------
static FIELD_OBJ_PTR GetPlayerFrontIce(FIELD_OBJ_PTR player_fldobj)
{
	int x,y,z;
	int dir;
	CONST_FIELD_OBJ_SYS_PTR sys;
	FIELD_OBJ_PTR fldobj;

	sys = FieldOBJ_FieldOBJSysGet( player_fldobj );
	//自機の向きを取得
	dir = FieldOBJ_DirDispGet( player_fldobj );
	//前方座標取得
	x = FieldOBJ_NowPosGX_Get( player_fldobj ) + FieldOBJ_DirAddValueGX( dir );
	y = FieldOBJ_NowPosGY_Get( player_fldobj );
	z = FieldOBJ_NowPosGZ_Get( player_fldobj ) + FieldOBJ_DirAddValueGZ( dir );
	//現在座標の前方を調べる
	fldobj = GetFldObj( sys, x, y, z );

	if (fldobj!=NULL){
		int disp_dir;
		//OBJコードを取得して、氷塊かどうかをチェックする
		if ( FieldOBJ_OBJCodeGet( fldobj ) != ICE ){
			return NULL;
		}
		//まだ固まっていない（上向きでない）氷塊かをチェックする
		disp_dir =  FieldOBJ_DirDispGet( fldobj );
		if ( disp_dir != DIR_DOWN ){
			GF_ASSERT( disp_dir == DIR_UP );	//下向きで無い場合は上向きしかありえない
			return NULL;
		}
		return fldobj;
	}else{
		return NULL;
	}
}

//--------------------------------------------------------------
/**
 * 指定位置のフィールドOBJを返す
 * @param	sys				フィールドOBJシステムポインタ
 * @param	x				X座標	グリッド
 * @param	y				Y座標	グリッド
 * @param	z				Z座標	グリッド
 * @retval	FIELD_OBJ_PTR	NULLの場合は検出失敗
 */
//--------------------------------------------------------------
static FIELD_OBJ_PTR GetFldObj( CONST_FIELD_OBJ_SYS_PTR sys, int x, int y, int z )
{
	int max,hx,hz;
	FIELD_OBJ_PTR fldobj;

	fldobj = FieldOBJSys_FldOBJWorkGet( sys );
	max = FieldOBJSys_OBJMaxGet( sys );

	do{
		if( FieldOBJ_StatusBit_Check(fldobj,FLDOBJ_STA_BIT_USE) ){
			if( FieldOBJ_StatusBit_Check(fldobj,FLDOBJ_STA_BIT_FELLOW_HIT_NON) == 0 ){
				hx = FieldOBJ_NowPosGX_Get( fldobj );
				hz = FieldOBJ_NowPosGZ_Get( fldobj );

				if( hx == x && hz == z ){
					int hy = FieldOBJ_NowPosGY_Get( fldobj );
					int sy = hy - y;
					if( sy < 0 ){ sy = -sy; }
					if( sy < H_GRID_FELLOW_SIZE ){
						return( fldobj );
					}
				}
			}
		}

		FieldOBJSys_FldOBJWorkInc( &fldobj );
		max--;
	}while( max );

	return( NULL );
}

//--------------------------------------------------------------
/**
 * 氷塊の移動
 * @param	imw			イベントワークポインタ
 *
 * @retval	BOOL			TRUE:移動終了	FALSE:移動継続
 */
//--------------------------------------------------------------
static BOOL MoveIce( ICE_MV_WORK *imw )
{
	int ret;

	switch(imw->Seq){
	case ICE_MOVE_SLIDE:
		//氷塊アニメ待ち
		if( FieldOBJ_AcmdEndCheck( imw->Ice ) ){
			//氷塊前方チェック
			int x = FieldOBJ_NowPosGX_Get( imw->Ice ) + FieldOBJ_DirAddValueGX( imw->MvDir );
			int y = FieldOBJ_NowPosGY_Get( imw->Ice );
			int z = FieldOBJ_NowPosGZ_Get( imw->Ice ) + FieldOBJ_DirAddValueGZ( imw->MvDir );
			ret = IceHitCheck( imw->Ice, x, y, z );
			if ( ret & ICE_HIT_OBJ ){	//障害物ヒット
				FIELD_OBJ_PTR fldobj;
				CONST_FIELD_OBJ_SYS_PTR sys;
				sys = FieldOBJ_FieldOBJSysGet( imw->Ice );
				//障害物ヒットした氷塊かチェック
				fldobj = GetFldObj( sys, x, y, z );
				if (fldobj == NULL){
					GF_ASSERT(0);
					return TRUE;
				}
				if( FieldOBJ_OBJCodeGet( fldobj ) == ICE ){	//氷塊
					imw->HitIce = fldobj;
					//方向固定解除
					FieldOBJ_AcmdSet(imw->Ice, AC_DIR_PAUSE_OFF);
					imw->Seq = ICE_MOVE_PAUSE_OFF_WAIT;
				}else {				//氷塊以外
					imw->HitIce = NULL;
					//方向固定解除
					FieldOBJ_AcmdSet(imw->Ice, AC_DIR_PAUSE_OFF);
					imw->Seq = ICE_MOVE_PAUSE_OFF_WAIT;
				}
			}else if( (ret & ICE_HIT_WALL)||(ret & ICE_HIT_NOICE_ATR) ){	//壁ヒット・氷床アトリビュート以外
				//これ以上進めないので、処理終了
				return TRUE;
			}else{	//氷床
				//アニメ発行
				int code;
				//方向固定
				FieldOBJ_AcmdSet(imw->Ice, AC_DIR_PAUSE_ON);
				imw->Seq = ICE_MOVE_PAUSE_ON_WAIT;
			}
		}
		break;
	case ICE_MOVE_PAUSE_ON_WAIT:
		//方向ポーズ終了待ち
		if ( FieldOBJ_AcmdEndCheck( imw->Ice ) ){
			int code;
			code = GetMoveAnmCode(imw->MvDir);
			if (code != ACMD_NOT){
				FieldOBJ_AcmdSet(imw->Ice, code);	//移動
			}else{
				GF_ASSERT(0);
				return TRUE;
			}

			imw->Seq = ICE_MOVE_SLIDE;
		}
		break;
	case ICE_MOVE_PAUSE_OFF_WAIT:
		//アニメ終了待ち
		if ( FieldOBJ_AcmdEndCheck( imw->Ice ) ){
			//氷固まるＳＥ
			Snd_SePlay(SEQ_SE_GS_HYOUKAI_KETUGOU);
			if (imw->HitIce != NULL){
				//前方氷塊を固まらせる
				FieldOBJ_AcmdSet(imw->HitIce, AC_DIR_U);
			}
			//自分自身を固まらせる
			FieldOBJ_AcmdSet(imw->Ice, AC_DIR_U);
			//これ以上進めないので、処理終了
			return TRUE;
		}
		break;
	}

	return FALSE;		//処理継続
}

//--------------------------------------------------------------
/**
 * 氷塊移動チェック
 * @param	fldobj	FIELD_OBJ_PTR
 * @param	inX		X座標
 * @param	inY		Y座標
 * @param	inZ		Z座標
 *
 * @retval	u32		ヒットビット ICE_HIT_NONE等
 */
//--------------------------------------------------------------
static u32 IceHitCheck( FIELD_OBJ_PTR fldobj,
						const int inX, const int inY, const int inZ )
{
	VecFx32 vec;
	u32 ret,hit;
	FieldOBJ_VecPosGet( fldobj, &vec );

	ret = ICE_HIT_NONE;

	{
		s8 flag;
		FIELDSYS_WORK *fsys = FieldOBJ_FieldSysWorkGet( fldobj );

		if( MPTL_CheckHitWallEx(fsys,&vec,inX,inZ,&flag) == TRUE ){
			ret |= ICE_HIT_WALL;
		}
	}

	{
		FIELDSYS_WORK *fsys = FieldOBJ_FieldSysWorkGet( fldobj );
		MATR attr = GetAttributeLSB( fsys, inX, inZ );
		if ( !MATR_IsIce(attr) ){
			ret |= ICE_HIT_NOICE_ATR;
		}
	}

	if( FieldOBJ_MoveHitCheckFellow(fldobj, inX, inY, inZ) == TRUE ){
		ret |= ICE_HIT_OBJ;
	}

	return( ret );
}

//--------------------------------------------------------------
/**
 * 指定方向の進行アニメコマンドを返す
 *
 * @param	inDir	方向
 *
 * @retval	int		アニメコード
 */
//--------------------------------------------------------------
static int GetMoveAnmCode(const u8 inDir)
{
	int ret = ACMD_NOT;

	switch(inDir){
	case DIR_UP:
		ret = AC_WALK_U_8F;
		break;
	case DIR_DOWN:
		ret = AC_WALK_D_8F;
		break;
	case DIR_LEFT:
		ret = AC_WALK_L_8F;
		break;
	case DIR_RIGHT:
		ret = AC_WALK_R_8F;
		break;
	default:
		GF_ASSERT(0);
	}
	return ret;
}

//--------------------------------------------------------------
/**
 * 強制移動後消去
 * @param	player	PLAYER_STATE_PTR
 * @retval	nothing
 */
//--------------------------------------------------------------
static void UnderForceMoveClear( PLAYER_STATE_PTR player )
{
	if( Player_MoveBitCheck_Force(player) == TRUE ){
		FIELD_OBJ_PTR fldobj = Player_FieldOBJGet( player );
		FieldOBJ_StatusBit_OFF( fldobj, FLDOBJ_STA_BIT_PAUSE_DIR|FLDOBJ_STA_BIT_PAUSE_ANM );

		if( Player_MoveBitCheck_ForceSaveSpeed(player) == FALSE ){
			Player_MoveSpeedClear( player );
		}

		Player_MoveBitSet_Force( player, FALSE );
		Player_MoveBitSet_ForceSaveSpeed( player, FALSE );
		Player_MoveBitSet_SandFloatSet( player, FALSE );
	}
}
